<?php
class System_Validate
{
    private $_data = array();
    private $_messages = array();
    private $_rules = array();
    private $_methods = array(
        'required' => 'System_Validate_Required',
        'email' => 'System_Validate_Email',
        'username' => 'System_Validate_Username',
        'date' => 'System_Validate_Date',
        'dateTime' => 'System_Validate_DateTime',
        'equal' => 'System_Validate_Equal',
        'minLength' => 'System_Validate_MinLength',
        'maxLength' => 'System_Validate_MaxLength',
        'minValue' => 'System_Validate_MinValue',
        'maxValue' => 'System_Validate_MaxValue',
        'in' => 'System_Validate_In',
    );

    protected static $_instance = null;

    public static function getInstance()
    {
        if (null === self::$_instance) {
            self::$_instance = new self();
        }

        return self::$_instance;
    }

    public function get($name, $default = null)
    {
        return isset($this->_data[$name]) ? $this->_data[$name] : $default;
    }

    public function set($name, $value)
    {
        $this->_data[$name] = $value;
        return $this;
    }

    public function isValid()
    {
        return count($this->_messages) == 0;
    }

    public function addMethod($method, $className)
    {
        $this->_methods[$method] = $className;
    }

    public function addRule($field, $method, $value = null, $message = null)
    {
        if(!isset($this->_methods[$method])) {
            throw new Exception('Undefined validator name ' . $method);
        }

        $this->_rules[$field][] = array(
            'method' => $method,
            'value' => $value,
            'message' => $message
        );
        return $this;
    }

    public function addRules($rules)
    {
        $this->_rules = array_merge($this->_rules, $rules);
    }

    public function removeRule($field, $method)
    {
        if(!isset($this->_rules[$field])) {
            return false;
        }
        if(!is_array($this->_rules[$field])) {
            return false;
        }
        foreach($this->_rules[$field] as $key => $rule) {
            if(isset($rule['method']) && ($rule['method'] == $method)) {
                unset($this->_rules[$key]);
            }
        }
    }

    public function setMessage($name, $value = null)
    {
        if(is_array($name)) {
            foreach($name as $key => $value) {
                $this->setMessage($key, $value);
            }
        } else {
            $this->_messages[$name] = $value;
        }
    }

    public function hasMessage($name = null)
    {
        return isset($this->_messages[$name]) ? true : false;
    }

    public function getMessage($name = null, $default = null)
    {
        if(empty($name)) {
            return $this->_messages;
        }
        return isset($this->_messages[$name]) ? $this->_messages[$name] : $default;
    }

    public function register($name, $validator)
    {
        $this->_methods[$name] = $validator;
    }

    public function validate($field, $data)
    {
        if($this->hasMessage($field)) {
            return false;
        }

        if(!isset($this->_rules[$field])) {
            return true;
        }

        foreach($this->_rules[$field] as $rule) {
            if($this->hasMessage($field)) {
                return false;
            }

            $validatorName = $this->_methods[$rule['method']];
            $validator = new $validatorName();
            $result = call_user_func_array(array($validator, 'isValid'), array($data, $rule['value']));
            if(!$result) {
                $this->setMessage($field, $rule['message']);
            }
        }

        return $this->isValid();
    }

    public function validateFields($fields, $data)
    {
        $validated = array();
        foreach($fields as $field) {
            $validated[$field] = isset($data[$field]) ? $data[$field] : null;
            $this->validate($field, $validated[$field]);
        }
        return $validated;
    }

    public function valid($data, $method, $value = null)
    {
        if(!isset($this->_methods[$method])) {
            throw new Exception('Undefined validator name ' . $method);
        }
        $validatorName = $this->_methods[$method];
        $validator = new $validatorName();
        return call_user_func_array(array($validator, 'isValid'), array($data, $value));
    }

}
